/**
 * 轮询器
 */
export class Poller<D = unknown> {
  static readonly TAG = 'Poller';
  protected _poll_timer: number = 0;
  protected _jid: number = 0;
  protected _interval: number = 5000;
  protected _polling: boolean = false;
  protected _loading: boolean = false;
  protected _error: any = null;
  protected _data: D | null = null;
  protected _loader: () => Promise<D> = async () => { throw new Error(`[${Poller.TAG}.loader] loader not set!`); };
  /** 当前轮询id，每次执行轮询+1 */
  get jid() { return this._jid; }
  get polling() { return this._polling; }
  get loading() { return this._loading; }
  get error() { return this._error; }
  get interval(): number { return this._interval; }
  set interval(v: number) { this._interval = v; }
  get data() { return this._data; }
  debug_id?: string;

  set_interval(v: number) { this._interval = v; return this; }
  set_loader(v: typeof this._loader) { this._loader = v; return this; }
  constructor(loader = this._loader) {
    this._loader = loader;
  }

  start() {
    if (this.debug_id) console.debug(`[${Poller.TAG}(${this.debug_id})::start]`)
    this._polling = true;
    if (!this._loading) this.load();
  }

  stop() {
    if (this.debug_id) console.debug(`[${Poller.TAG}(${this.debug_id})::stop]`)
    this._poll_timer && window.clearTimeout(this._poll_timer);
    this._poll_timer = 0;
    this._jid++;
    this._loading = false;
    this._polling = false;
  }

  load() {
    if (this._loading) {
      console.warn(`[${Poller.TAG}.load] already loading, this call will be ignored.`);
      return;
    }
    this._loading = true;
    const { _polling: _is_polling } = this;
    this._jid += 1
    const poll_jid = this._jid;

    this._loader().then((data) => {
      if (this.debug_id && poll_jid !== this._jid) console.debug(`[${Poller.TAG}(${this.debug_id})::load] resolve but jid changed`)
      if (poll_jid !== this._jid) return;
      if (this.debug_id) console.debug(`[${Poller.TAG}(${this.debug_id})::load] resolve`)
      this._data = data;
      delete this._error;
    }).catch(e => {
      if (this.debug_id && poll_jid !== this._jid) console.debug(`[${Poller.TAG}(${this.debug_id})::load] reject but jid changed`)
      if (poll_jid !== this._jid) return;
      if (this.debug_id) console.debug(`[${Poller.TAG}(${this.debug_id})::load] reject`)
      this._error = e;
    }).finally(() => {
      if (this.debug_id && poll_jid !== this._jid) console.debug(`[${Poller.TAG}(${this.debug_id})::load] finally but jid changed`)
      if (poll_jid !== this._jid) return;
      if (this.debug_id) console.debug(`[${Poller.TAG}(${this.debug_id})::load] finally`)
      this._loading = false;
      if (_is_polling) this._poll_timer = window.setTimeout(() => this.load(), this.interval);
    });
  }
}
